/*
Copyright 2010 Peter Krajnc, SCREENWORKER

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.


Example:

package com.screenworker.example
{

import com.screenworker.compactmvc.extensions.factoryservice.FactoryService;
import com.screenworker.compactmvc.extensions.interfaces.IFactoryService;
import com.screenworker.compactmvc.patterns.Controller;

import flash.display.MovieClip;
import flash.system.ApplicationDomain;
import com.screenworker.helpers.ExampleMC;

public class MyFactoryModel extends FactoryService implements IFactoryService
{
public static const NAME:String = 'MyFactoryModel';

[Embed(source="./assets/example.swf", mimeType='application/octet-stream')]
public var embedswf:Class;

private var _applicationDomain:ApplicationDomain;

public var view:MyViewComponent;

public function MyFactoryModel(name:String, controller:Controller)
{
super(name, controller);
}

override public function registerHandler():void
{
addByteArray('asset', embedswf);
start();
}

override public function completeHandler():void
{	
_applicationDomain = getResource('asset').applicationDomain;
reset();

//Instance defined in embeded swf
var mc:ExampleMC = new ExampleMC(); //UIComponent

view.addChild(mc);

}

public function getAsset( key:String ):MovieClip
{
if(_applicationDomain == null)
return null;

var symbolRef:Class = Class( _applicationDomain.getDefinition( key ) );
var asset:MovieClip = MovieClip( new symbolRef() );
return asset;
}

}
}
*/
package com.screenworker.compactmvc.extensions.factoryservice
{
	import com.screenworker.compactmvc.extensions.factoryservice.events.FactoryServiceEvent;
	import com.screenworker.compactmvc.extensions.factoryservice.resourcetypes.ResourceByteArray;
	import com.screenworker.compactmvc.extensions.interfaces.IFactoryService;
	import com.screenworker.compactmvc.extensions.interfaces.IResourceType;
	import com.screenworker.compactmvc.interfaces.IModel;
	import com.screenworker.compactmvc.patterns.Controller;
	import com.screenworker.compactmvc.patterns.Model;
	
	import flash.events.EventDispatcher;
	import flash.events.TimerEvent;
	import flash.system.LoaderContext;
	import flash.utils.ByteArray;
	import flash.utils.Timer;

	public class FactoryService extends Model implements IModel, IFactoryService
	{
		private static const TIMER_DELAY:int = 20;
		private var _name:String;
		private var _resources:Array;
		private var _progress:Number = 0;
		private var _stepPosition:int = -1;
		private var _timer:Timer;
		private var _currentResource:IResourceType;
		
		public function FactoryService(name:String, controller:Controller)
		{
			super(name, controller);
			
			_resources = new Array();
			_timer = new Timer(TIMER_DELAY);
			_timer.addEventListener(TimerEvent.TIMER, timerEventHandler, false, 0, true);
		}
				
		public function errorHandler(message:String):void
		{
			//overriden by subclass
		}
		
		public function completeHandler():void
		{
			//overriden by subclass
		}
		
		public function stepCompleteHandler(position:int, resourceName:String):void
		{
			//overriden by subclass
		}
		
		public function progressHandler(position:int, progress:Number, resourceName:String):void
		{
			//overriden by subclass
		}
		
		protected function reset():void
		{
			_timer.reset();
			_timer = null;
			for each(var resource:IResourceType in _resources)
			{
				resource.reset();
				resource = null;
			}
			_resources = null;
		}
		
		protected function addByteArray(name:String, byteArray:Class, priority:int=0):void
		{
			//allowLoadBytesCodeExecution activation in air
			var loaderContext:* = new LoaderContext();
			if(loaderContext.hasOwnProperty("allowLoadBytesCodeExecution"))
			{
				loaderContext.allowLoadBytesCodeExecution = true;
			}	
			
			_resources.push( new ResourceByteArray(name, new byteArray() as ByteArray, loaderContext) );
		}
		
		protected function addFrameTwo(name:String, position:int, resource:IResourceType):void
		{
		}
		
		protected function addRemote(position:int, resource:IResourceType):void
		{
		}
		
		protected function getResource(name:String):IResourceType
		{
			
			for each(var resource:IResourceType in _resources)
			{
				if(resource.resourceName == name)
					return resource;
			}
			throw new Error('Resource with name: ' + name + ' not found!');
			return null;
		}
		
		private function getResourceByPosition(position:uint):IResourceType
		{		
			if(_resources[position] == null)
				throw new Error('Resource at position: ' + position + ' not found!');
				
			return _resources[position];
		}
		
		protected function start():void
		{
			if(totalSteps == 0)
				throw new Error("no resources added!");
			if(_currentResource != null)
				throw new Error("Service has already startet!");
			nextStep();
		}
		
		private function nextStep():Boolean
		{
			if(_stepPosition == totalSteps && _resources.length == 0)
				return false;

			//break if current step has not finished!
			if(_currentResource != null)
			{	
				if(!_currentResource.isComplete)
					return false;
			}
			
			_stepPosition ++;
			
			_currentResource = null;
			_currentResource = _resources[_stepPosition];
			EventDispatcher(_currentResource).addEventListener(FactoryServiceEvent.COMPLETE, resourceCompleteHandler, false, 0, true);
			_currentResource.startLoading();
			
			if(!_timer.running)
				_timer.start();
			
			return true;
		}
		
		private function timerEventHandler(e:TimerEvent):void
		{
			if(_currentResource == null)
				return;
			
			var globalProgress:Number = 0;
			var globalComplete:uint = 0;
			var proggressResources:Array = _resources.slice(0, _stepPosition);
			for each(var item:IResourceType in proggressResources)
			{
				if(item != null)
				{
					globalProgress += item.progress;
					globalComplete += item.isComplete ? 1 : 0;
				}
				
			}		
			_progress = globalProgress/totalSteps;
			
			//dispatch progress event
			progressHandler(_stepPosition, _progress, _currentResource.resourceName);
				
			if(stepPosition == totalSteps - 1 && _currentResource.isComplete)
			{		
				_timer.stop();
				completeHandler();
			}
		}	
		
		private function resourceCompleteHandler(e:FactoryServiceEvent):void
		{
			if(e.resourceName != _currentResource.resourceName)
				return;
			
			//dispatch stepComplete
			stepCompleteHandler(_stepPosition, _currentResource.resourceName);
		}
		
		protected function get stepPosition():Number
		{
			return _stepPosition;
		}
		
		protected function get totalSteps():Number
		{
			return _resources.length;
		}
		
		protected function get proggress():Number
		{
			return _progress;
		}
		
		protected function get name():String
		{
			return _name;
		}
		
	}
}
